package com.payroll.framework;

import org.apache.struts.action.*;
import org.apache.log4j.*;
/*******************************************************
 *                                                     *
 * AMS CONFIDENTIAL AND PROPRIETARY INFORMATION        *
 * (C) 2002 AMERICAN MANAGEMENT SYSTEMS, INCORPORATED  *
 * DO NOT COPY OR DISLCOSE                             *
 * ALL RIGHTS RESERVED                                 *
 *                                                     *
 *******************************************************
*/
import com.payroll.framework.util.*;

import java.sql.*;
import java.io.*;

/**
 * Central class that handles and process all AMSFoundation errors. This class maintains the master hashmap of
 * all entries in the EXC_MESSAGE table. It is a singleton class and for the application developer to get a handle
 * to the object you have to perform the following:<br/>
 * <code>PayrollErrorManager err = (PayrollErrorManager) PayrollErrorManager.getInstance()</code>
 * <br/>
 * @author: S Popovic
 */
public final class PayrollErrorManager implements  Serializable {
	private java.util.HashMap i_ErrorHashMap;

	/** Variable to implement singleton behaviour
	 */
	protected static PayrollErrorManager _instance;
	/**
	 * PayrollErrorManager default constructor
	 */
	public PayrollErrorManager() {
		super();
	}
	/**
	 * Substitutes input string containing  %1, %2, %n with subsequent substitutions in the substitution array.
	 * @param p_Input The string containing substitution placeholders.
	 * @param p_Substitutions[] String array containing substitution values.
	 * @return A formatted string containing the substitution values.  If there are no substitution values, the input
	 * string is returned unchanged.
	 * @author S Popovic, modified from iFile framework
	 */
	private String doSubstitutions(String p_Input, String p_Substitutions[]) {
		if (p_Input == null)
			return null;

		if (p_Substitutions == null || p_Substitutions.length == 0) {
			return p_Input;
		}

		//int numberOfSubstitutes = 0;
		//if (p_Substitutions != null)
		//	numberOfSubstitutes = p_Substitutions.length;
		int substituttionsToBeMade = 0;
		StringBuffer m_InputSubstituted = new StringBuffer(p_Input);
		for (int index = 0; index < m_InputSubstituted.length(); index++) {
			if (m_InputSubstituted.charAt(index) == '%')
				substituttionsToBeMade++;
		}

		//if (numberOfSubstitutes == substituttionsToBeMade)
		//if (numberOfSubstitutes > 0)
		if (substituttionsToBeMade > 0) {
			int firstIndex = p_Input.indexOf('%');
			m_InputSubstituted = new StringBuffer(p_Input.substring(0, firstIndex));
			m_InputSubstituted = m_InputSubstituted.append(p_Substitutions[0]);
			m_InputSubstituted =
				m_InputSubstituted.append(p_Input.substring(firstIndex + 2));
			for (int i = 1; i < substituttionsToBeMade; i++) {

				int nextIndex = m_InputSubstituted.toString().indexOf("%", firstIndex + 1);
				StringBuffer newMessage = m_InputSubstituted;
				m_InputSubstituted =
					new StringBuffer(m_InputSubstituted.toString().substring(0, nextIndex));

				m_InputSubstituted = m_InputSubstituted.append(p_Substitutions[i]);
				if (nextIndex + 2 < newMessage.length())
					m_InputSubstituted =
						m_InputSubstituted.append(newMessage.toString().substring(nextIndex + 2));
				firstIndex = nextIndex;
			}
		}

		return m_InputSubstituted.toString();
	}
	/**
	 * Performs a master look-up in the error hashmap object and returns the corresponding ErrorBean.
	 * @return An ErrorBean object that contains the appropriate values from the EXC_MESSAGE table.
	 * @param p_ErrorCode The ID_EXC_MESSAGE from the EXC_MESSAGE table.
	 */
	public ErrorBean getErrorBean(int p_ErrorCode) throws PayrollFoundationException {
		return (ErrorBean) getErrorHashMap().get(new Integer(p_ErrorCode));
	}
	/**
	 * Returns the error hashmap, a hashmap keyed by ID_EXC_MESSAGE containing ErrorBeans.  Each ErrorBean represents
	 * one row of data in the EXC_MESSAGE table.
	 * @return A hashmap containing ErrorBean objects.
	 * @author S Popovic
	 */
	private java.util.HashMap getErrorHashMap() throws PayrollFoundationException {
		return i_ErrorHashMap;

	}
	/**
	 * Returns the message from the EXC_Message table if it exists. Otherwis it returns a default message stating
	 * the message does not exist with the provided key.
	 * @return An ErrorBean containing a properly formatted error message.
	 * @param p_afe The exception from which to retrieve the error code and substitution values.
	 * @author S Popovic
	 */
	public String getEXCMessage(String p_ErrorKey) {

		try {
			ErrorBean m_ReturnErrorBean =
				(ErrorBean) getErrorHashMap().get(new Integer(p_ErrorKey));
			if (m_ReturnErrorBean != null) {
				return m_ReturnErrorBean.getTX_USER_DS();
			} else {
				return ("No EXC_MESSAGE was found with ID " + p_ErrorKey);
			}
		}

		// problem with the error hashmap. Only thing that will cause this is a problem with the database.
		catch (PayrollFoundationException afe) {
			return "A fatal exception occurred while initializing the error hash map. "
				+ "This usually occurs when the Error Manager is unable to connect to the database or a general database exception "
				+ "has occurred.  Contact your system administrator and reference the above error code from EXC_MESSAGE. ";
		}
		// problem with the error hashmap. Probably caused by the database being down or a complete system meltdown.
		catch (Exception e) {
			return "An unknown exception occured while initializing the error hash map. "
				+ "Please contact your system administrator";
		}
	}
	/**
	 * Returns an ErrorBean containing a formatted message.  A formatted message is one where all substitutions are
	 * populated with data. This is the main workhorse of all foundation error processing. The basic operation is
	 * converting a thrown exception into a readable error bean format.
	 * @return An ErrorBean containing a properly formatted error message.
	 * @param p_afe The exception from which to retrieve the error code and substitution values.
	 * @author S Popovic
	 */
	public ErrorBean getFormattedErrorBean(PayrollFoundationException p_afe) {
		try {
			// find the exception ID in the error hashmap and format it for placement in the error bean.
			ErrorBean m_ReturnErrorBean =
				(ErrorBean) getErrorHashMap().get(new Integer(p_afe.getI_ErrorCode()));
			if (m_ReturnErrorBean != null) {
				m_ReturnErrorBean.setI_FormattedMessage(
					doSubstitutions(m_ReturnErrorBean.getTX_USER_DS(), p_afe.getI_Substitutions()));
				m_ReturnErrorBean.setI_ClassAndMethodName(p_afe.getI_ClassAndMethodName());
				return m_ReturnErrorBean;
			}
			/* The error code thrown by the exception could not be found in the error hash map. Most likely cause is
			someone threw the error without entering it in the database. The other cause could be a hashmap collision
			while initializing the error hash map. */
			else
				m_ReturnErrorBean = new ErrorBean();
			/* If there is no error code in the exception thrown, this is user defined error message and
			should be processed	differently */
			if (p_afe.getI_ErrorCode() == 0) {
				m_ReturnErrorBean.setI_FormattedMessage(p_afe.getMessage());
				m_ReturnErrorBean.setI_ClassAndMethodName(p_afe.getI_ClassAndMethodName());
				return m_ReturnErrorBean;
			} else {
				m_ReturnErrorBean.setI_FormattedMessage(
					"Error Code "
						+ p_afe.getI_ErrorCode()
						+ " was not found in the "
						+ "database.  Please contact your system administrator and reference the above error code.");
				m_ReturnErrorBean.setI_ClassAndMethodName(p_afe.getI_ClassAndMethodName());
				m_ReturnErrorBean.setID_EXC_MESSAGE(p_afe.getI_ErrorCode());
				return m_ReturnErrorBean;
			}
		}
		// problem with the error hashmap. Only thing that will cause this is a problem with the database.
		catch (PayrollFoundationException afe) {
			ErrorBean m_ReturnErrorBean = new ErrorBean();
			m_ReturnErrorBean.setID_EXC_MESSAGE(afe.getI_ErrorCode());
			m_ReturnErrorBean.setI_ClassAndMethodName(
				"PayrollErrorManager::getFormattedErrorBean");
			m_ReturnErrorBean.setI_FormattedMessage(
				"A fatal exception occurred while initializing the error hash map. "
					+ "This usually occurs when the Error Manager is unable to connect to the database or a general database exception "
					+ "has occurred.  Contact your system administrator and reference the above error code from EXC_MESSAGE. ");
			return m_ReturnErrorBean;
		}
		// problem with the error hashmap. Probably caused by the database being down or a complete system meltdown.
		catch (Exception e) {
			ErrorBean m_ReturnErrorBean = new ErrorBean();
			m_ReturnErrorBean.setID_EXC_MESSAGE(0);
			m_ReturnErrorBean.setI_ClassAndMethodName(
				"PayrollErrorManager::getFormattedErrorBean");
			m_ReturnErrorBean.setI_FormattedMessage(
				"An unknown exception occured while initializing the error hash map. "
					+ "Please contact your system administrator");
			return m_ReturnErrorBean;
		}
	}
	/**
	 * Returns an ErrorBean containing a formatted message.  A formatted message is one where all substitutions are
	 * populated with data. This is the main workhorse of all foundation error processing. The basic operation is
	 * converting a thrown exception into a readable error bean format.
	 * @return An ErrorBean containing a properly formatted error message.
	 * @param p_afe The exception from which to retrieve the error code and substitution values.
	 * @author S Popovic
	 */
	public String getFormattedErrorMessage(ActionError p_ActionError) {
		String m_FormattedError = "";

		try {
			Object[] m_ObjectSubs = new Object[4];
			// find the exception ID in the error hashmap and format it for placement in the error bean.
			ErrorBean m_ReturnErrorBean =
				(ErrorBean) getErrorHashMap().get(new Integer(p_ActionError.getKey()));
			if (m_ReturnErrorBean != null) {
				// get the substitutions from the ActionError
				m_ObjectSubs = p_ActionError.getValues();
				String[] m_Substitutions = new String[m_ObjectSubs.length];
				for (int i = 0; i < m_ObjectSubs.length; i++) {
					String m_Substitution = (String) m_ObjectSubs[i];
					if (m_Substitution != null && m_Substitution.length() > 0) {
						m_Substitutions[i] = m_Substitution;
					}
				}

				m_FormattedError =
					doSubstitutions(m_ReturnErrorBean.getTX_USER_DS(), m_Substitutions);
				return m_FormattedError;

			} else {
				/* The error code thrown by the exception could not be found in the error hash map. Most likely cause is
				someone threw the error without entering it in the database. The other cause could be a hashmap collision
				while initializing the error hash map. */
				m_FormattedError =
					"Error Code "
						+ p_ActionError.getKey()
						+ " was not found in the "
						+ "database.  Please contact your system administrator and reference the above error code.";
				return m_FormattedError;
			}
		}
		// problem with the error hashmap. Only thing that will cause this is a problem with the database.
		catch (PayrollFoundationException afe) {
			m_FormattedError =
				"A fatal exception occurred while initializing the error hash map. "
					+ "This usually occurs when the Error Manager is unable to connect to the database or a general database exception "
					+ "has occurred.  Contact your system administrator and reference the above error code from EXC_MESSAGE. ";
			return m_FormattedError;
		}
		// problem with the error hashmap. Probably caused by the database being down or a complete system meltdown.
		catch (Exception e) {
			m_FormattedError =
				"An unknown exception occured while initializing the error hash map. "
					+ "Please contact your system administrator";
			return m_FormattedError;
		}
	}

	/**
	 * Loads the ErrorBean hashmap by populating values from the database.
	 * @see AMSSingletonInstanceBase#loadCache()
	 * @author S Popovic
	 */
	public void loadCache() throws PayrollFoundationException {

		Category cat =
			Category.getInstance(this.getClass().getName() + "refreshErrorHashMap");
		cat.debug("entering");
		Connection m_Connection = null;
		Statement m_Statement = null;
		ResultSet m_ResultSet = null;
		int m_ErrorKey = 0;
		setInstance(this);
		i_ErrorHashMap = new java.util.HashMap(2000, .70f);
		ErrorBean m_ErrorBean = new ErrorBean();
		m_ErrorKey = 100021;
		m_ErrorBean.setID_EXC_MESSAGE(100021);
		m_ErrorBean.setTX_USER_DS("Username");
		i_ErrorHashMap.put(new Integer(m_ErrorKey), m_ErrorBean);
	}

	/** This method lets the class know which instance has been loaded in memory.
	 * This method is called from loadCache() and reLoadCache() methods in the
	 * ancestor.
	 * @param p_thisinstance
	 */
	protected void setInstance(PayrollErrorManager p_thisinstance) {
		Category cat =
			Category.getInstance(
				this.getClass().getName()+ ".setInstance");

		cat.debug ("Setting instance to" + p_thisinstance.getClass().getName());
		_instance = p_thisinstance;
	}

		/** @return PayrollErrorManager
		*/
		public synchronized static PayrollErrorManager getInstance() {
			Category cat =
				Category.getInstance("com.payroll.framework.PayrollErrorManager.getInstance");

			if (_instance == null) {
				// Set the instance to this in case the descendant does not set
				// the value to it`
				PayrollErrorManager m_localInstance = new PayrollErrorManager();
				try {
					m_localInstance.loadCache();
				} catch (PayrollFoundationException e) {
					cat.error("Error loading cache: " + e.getMessage());
				}

			}
			return _instance;
		}
		/** This method sets the current instance to null and creates a new instance of the class
		*  set by the setInstance method and calls the loadCache method on newly created instance.
		*  This code currently does not support cluster environment but will be enhanced to implement
		*  that logic.
		* @throws PayrollFoundationException if unable to create a new instance of PayrollErrorManager
		*/
		public void reLoadCache() throws PayrollFoundationException {
			//Category cat = Category.getInstance(this.getClass().getName() + ".reLoadCache");

			try {
				PayrollErrorManager m_localInstance = new PayrollErrorManager();
				m_localInstance.loadCache();
			} catch (Exception e) {
				throw new PayrollFoundationException(
					this.getClass().getName() + ".reLoadCache",
					e.getMessage());
			}
		}
public String getWidgetHelp(String p_Path, String p_Action) throws PayrollFoundationException {

	return "";

}

	public boolean isSerialized() throws Exception {
		String m_localFileName;
		m_localFileName = Foundation.getProperty("errmCacheFileName");
		Category cat = Category.getInstance("com.payroll.framework.PayrollErrorManager.isSerialized");

		File testFile;

		//Check to see if any file was specified at all
		try {
			testFile = new File(m_localFileName);
		} catch (NullPointerException npe) {
			cat.debug( "errmCacheFileName not available, cannot load local cache");
			return false;
		}

		//Create the path directory if it does not already exist
		try {
			if (testFile.getParentFile().mkdirs()) {
				cat.debug ("Cache directory does not currently exist, creating...");
				return false;
			}
		}
		//This part is useless until SecurityManager is enabled, however it should
		//be noted that if any directory in the cache path has denied permissions,
		//the whole process will fail in the end
		//catch (SecurityException se) {
		catch (Exception e) {
			cat.debug ("Cannot create directory " + testFile.getParent() + "for local cache");
			cat.debug ("Check file permissions to allow creation.");
			return false;
		}

		//Directory exists here, load the file from cache if exists, exit otherwise
		try {
			FileInputStream istream = new FileInputStream(m_localFileName);
			ObjectInputStream p = new ObjectInputStream(istream);

			cat.debug("Local error cache file exists, loading...");

			PayrollErrorManager errm = (PayrollErrorManager) p.readObject();
			setInstance(errm);
			p.close();
			istream.close();

			cat.debug("Error cache loaded successfully from serialized file");
			return true;
		}
		catch (FileNotFoundException fe) {
			cat.debug("Local error cache file not found, creating...");
			return false;
		}
	}


	public void serializeErrm() throws Exception {
		String m_localFile;
		m_localFile = Foundation.getProperty("errmCacheFileName");
		Category cat = Category.getInstance("com.payroll.framework.PayrollErrorManager.SerializeErrm");

		if (m_localFile == null) {
			cat.debug("errmCacheFileName name not defined in resource bundle. Error Manager will not be serialized.");
		} else {
			try {
				FileOutputStream ostream =
					new FileOutputStream(m_localFile);
				ObjectOutputStream p = new ObjectOutputStream(ostream);

				p.writeObject(this);

				p.flush();
				ostream.close();
				p.close();
			} catch (Exception e) {
				throw e;
			}
		}
	}

}
